/**
 * @class 可观测宇宙对象实体
 * @author DuskyHuang 圣子
 * @description 这是最大的一个尺度范围，本类负责该尺度内的渲染和逻辑。
*/

import { _decorator, instantiate, Node, Vec4, Material, MeshRenderer } from 'cc';
import { core } from 'cfw';
import { LAYER_MAIN_3D, DEG_PERIGON, Res } from 'const';
import { createNodeAt, setParticles } from 'helpers';
import { Task, TaskNames } from 'notifications';
import { cosmos } from 'proxies';
import { CosmicScope } from './cosmic-scope';
const { ccclass } = _decorator;

@ccclass('cocosmos.ObservableCosmos')
export class ObservableCosmos extends CosmicScope {

    protected _substructure_key: substructures = 'great-walls';
    protected _attenuation: attenuation = [9, 9.5, 10.5, 11.2];
    protected PrefabKey: string = Res.Prefabs.ACANTHO;
    protected offsetY: number = 0.45;

    public update(dt?: time.sec): void {
        super.update(dt);
        this._updateGreatWalls();
    }

    protected approachScope1stTime(): void {
        core.notify(Task.Load.DYNAMIC_TEX, TaskNames.TEX_SPR_CLUSTERS);
    }

    private _updateGreatWalls(): void {
        const { pow10 } = cosmos;
        if (cosmos.super) {
            if (this._pervWalls.children.length <= 0) this._createPerversiveGreatWalls();
            const [FLOOR, CEIL] = CLUSTERS_RANGE;
            const a = pow10 - FLOOR, b = CEIL - pow10, c = a / (a + b);
            if (b <= 0) {
                const s = Math.pow(10, b);
                this._pervWalls.setWorldScale(s, s, s);
            } else {
                this._gradient(this._pervWalls, pow10);
                this._pervWalls.setWorldScale(1, 1, 1);
            }
            clusters.x = clusters.y = c + 1;
            // clusters.x = clusters.y = 1;                     // Tiling
            clusters.z = Math.clamp01(a) * 0.8;                 // Strength
            clusters.w = Math.lerp(0.5, 0.1, Math.clamp01(c));  // Fading
            this._c_mat.passes[0].setUniform(this._c_handle, clusters);
            this._pervWalls.active = true;
            if (pow10 >= FAR_GW_START) {
                if (this._farWalls.children.length <= 0) this._createFarGreatWalls();
                this._farWalls.active = true;
            } else this._farWalls.active = false;
        }
    }

    protected createSubParticles(): void {
        this._subparts = createNodeAt("Perversive  Galaxy-Clusters", LAYER_MAIN_3D, this.node);
        const GalaxiesGUP = core.res.getPrefab(GALAXIES_GPU);
        [FarSC, VeryFarSC].forEach(args => setParticles(instantiate(GalaxiesGUP), args, this._subparts));
    }

    protected init(): void {
        this._farWalls = createNodeAt("Far Great-Walls", LAYER_MAIN_3D, this.node);
        this._pervWalls = createNodeAt("Perversive Great-Walls", LAYER_MAIN_3D, this.node);
    }

    private _createFarGreatWalls(): void {
        const ANGLE = DEG_PERIGON / RANDOM_NUM;
        const [FROM, TO] = DIST_RANGE;
        const SuperClusters = core.res.getPrefab(SUPER_CLUSTERS);
        for (let i = 0; i < RANDOM_NUM; i++) {
            const dist = Math.randomRange(FROM, TO);
            const radius = dist * RADIUS_RAT;
            const cluster = instantiate(SuperClusters);
            const rad = Math.parseRadian(i * ANGLE);
            cluster.setPosition(dist * Math.sin(rad), Math.randomRange(-dist / 2, dist / 2), dist * Math.cos(rad));
            cluster.setScale(radius, radius + Math.randomRange(0.2, 0.5), radius * 1.4);
            cluster.setRotationFromEuler(Math.randomRange(0, 360), 0, 90);
            cluster.parent = this._farWalls;
        }
        for (let i = 0; i < 2; i++) {
            const cluster = instantiate(SuperClusters);
            cluster.setPosition(0, (i === 0 ? 1 : -1) * TO, 0);
            const radius = FROM * RADIUS_RAT;
            cluster.setScale(radius, radius, radius * 1.5);
            cluster.setRotationFromEuler(0, Math.randomRange(0, 360), 0);
            cluster.parent = this._farWalls;
        }
    }

    private _createPerversiveGreatWalls(): void {
        const rot = DEG_PERIGON / GRADIENT_NUM
        const SuperClusters = core.res.getPrefab(SUPER_CLUSTERS);
        for (let i = 0; i < GRADIENT_NUM; i++) {
            const cluster = instantiate(SuperClusters);
            cluster.setRotationFromEuler(0, i * rot, i * rot / 2);
            cluster.parent = this._pervWalls;
            if (i === 0) {
                this._c_mat = cluster.getComponent(MeshRenderer)!.sharedMaterial;
                this._c_handle = this._c_mat.passes[0]?.getHandle('clusters');
            }
        }
    }

    private _gradient(container: Node, pow10: pow10, offset: num.posInt = 0): void {
        const pow2 = Math.log2(Math.pow(10, pow10));
        container.children.forEach((child, idx) => {
            let p = idx - pow2;
            p %= 5;
            const s = Math.pow(2, p + offset);
            child.setScale(s, s, s);
        });
    }

    private _farWalls: Node;
    private _pervWalls: Node;
    private _c_mat: Material = null;
    private _c_handle: number;
}


const DIST_RANGE = [220, 350];
const RADIUS_RAT = 0.2;
const RANDOM_NUM = 8;

const { Prefabs: { GALAXIES_GPU, SUPER_CLUSTERS } } = Res;
const GRADIENT_NUM = 5;
const CLUSTERS_RANGE = [8.5, 10];
const FAR_GW_START = 9;

const clusters = new Vec4;
const FarSC: IParticleLike = {
    num: 20000,
    sizes: [0.15, 0.3],
    radius: 100,
    thickness: 0.7,
    random: 0.5,
};

const VeryFarSC: IParticleLike = {
    num: 30000,
    sizes: [0.3, 0.6],
    radius: 400,
    thickness: 0.8,
    random: 0.5,
};
